//
// Created by tys on 2017/10/26.
//


#include <log.h>
#include <pthread.h>
#include <android/native_window_jni.h>
#include "include/Play.h"
#include "include/VideoMedia.h"
#include "include/AudioMedia.h"

extern "C" {
#include <libavutil/time.h>
#include <unistd.h>
}

/**
 * 获取AVCodecContext，设置默认值
 * */
int open_codec_context(AVMediaType media_type, MultiMedia *media);

/** 音频，视频packet入队列 */
void put_packet_que();

/**视频，音频解码器上下文*/
int init_ffmpeg(const char *url);

/**native window init*/
void init_native_window(JNIEnv *env, jobject surfaceView, jint i, jint i1);

/**播放，音频，视频入队列，并开启视频，音频播放线程*/
void play();

/**native window 播放 frame*/
void play_video_call_back(AVFrame *frame, VideoMedia *video);

/** Initialize one data packet for reading or writing. */
void init_packet(AVPacket *packet);

/*************************************************************************************************/
AudioMedia *audio = new AudioMedia();
VideoMedia *video = new VideoMedia(play_video_call_back);
const char *TAG = "play_film";
AVFormatContext *fmt_ctx;
bool isPlay = 0;
ANativeWindow *window;


void play_video_call_back(AVFrame *frame, VideoMedia *video) {
    if (!window) {
        return;
    }
    ANativeWindow_Buffer window_buffer;
    if (ANativeWindow_lock(window, &window_buffer, 0)) {
        return;
    }
//    ANativeWindow_setBuffersGeometry(window, audio->dec_ctx->width, audio->dec_ctx->height,
//                                     WINDOW_FORMAT_RGBA_8888);
    uint8_t *dst = (uint8_t *) window_buffer.bits;
    int dstStride = window_buffer.stride * 4;
    uint8_t *src = frame->data[0];
    int srcStride = frame->linesize[0];
    for (int i = 0; i < window_buffer.height; ++i) {
        memcpy(dst + i * dstStride, src + i * srcStride, srcStride);
    }
    ANativeWindow_unlockAndPost(window);
}


void init_packet(AVPacket *packet) {
//    av_init_packet(packet);
    /** Set the packet data and size so that it is recognized as being empty. */
    packet = av_packet_alloc();
    packet->data = NULL;
    packet->size = 0;
}

int play_film(const char *url) {

    /**************ffmpeg init****************/
    fmt_ctx = avformat_alloc_context();
    init_ffmpeg(url);

    /**************视频，音频压缩数据入队列, 开启播放线程****************/
    play();

//    avformat_free_context(fmt_ctx);
//    delete audio;
//    delete video;
    return 0;
}

int init_ffmpeg(const char *url) {
    int ret = -1;
    /* register all formats and codecs */
    av_register_all();
    avformat_network_init();
    /* open input file, and allocate format context */
    if ((ret = avformat_open_input(&fmt_ctx, url, NULL, NULL)) < 0) {
        LOG_E(TAG, "Could not open source file %s\n", url);
        return ret;
    }
    /* retrieve stream information */
    if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
        LOG_E(TAG, "%s", "Could not find stream information\n");
        return ret;
    }


    if ((ret = open_codec_context(AVMEDIA_TYPE_AUDIO, audio) >= 0)) {
        LOG_E(TAG, "%s\n", "获取音频解码器上下文成功 ");
    } else {
        goto cleanup;
    }

    if ((ret = open_codec_context(AVMEDIA_TYPE_VIDEO, video) >= 0)) {
        LOG_E(TAG, "%s\n", "获取视频解码器上下文成功 ");
    } else {
        goto cleanup;
    }

    video->setAudio(audio);
    LOG_E(TAG, "音频: index:%d, 解码器:dec_ctx:%p, %s,編碼:%s, base_time:%f\n", audio->stream_index,
          audio->dec_ctx, av_get_sample_fmt_name(audio->dec_ctx->sample_fmt),
          avcodec_get_name(audio->dec_ctx->codec_id),
          av_q2d(video->time_base));

    LOG_E(TAG, "视频: index:%d, 解码器:dec_ctx:%p,编码:%s,  %dx%d, base_time:%f\n", video->stream_index,
          video->dec_ctx,
          avcodec_get_name(video->dec_ctx->codec_id), video->dec_ctx->width, video->dec_ctx->height,
          av_q2d(video->time_base));

    return ret;
    cleanup:
    LOG_E("---", "%s\n", "异常");
    if (audio->dec_ctx)
        avcodec_free_context(&audio->dec_ctx);
    if (video->dec_ctx)
        avcodec_free_context(&video->dec_ctx);
    if (fmt_ctx)
        avformat_close_input(&fmt_ctx);

    return ret;
}

void init_native_window(JNIEnv *env, jobject surfaceView, jint w, jint h) {
    if (window) {
        ANativeWindow_release(window);
        window = 0;
    }
    window = ANativeWindow_fromSurface(env, surfaceView);
    ANativeWindow_setBuffersGeometry(window, w, h,
                                     WINDOW_FORMAT_RGBA_8888);
    if (video) {
        video->dst_pic_w = w;
        video->dst_pic_h = h;
    }
}

void play() {

    audio->play();

    video->play();

    put_packet_que();//生产


}


void play_pause(){
    isPlay = 0; //生产
    audio->setIsPlay(0);//消费
    video->setIsPlay(0);//消费
}

void *read_forms(void *arg) {
    LOG_E(TAG, "%s, %ld\n", "开始生产", pthread_self());
    AVPacket pkt;
    init_packet(&pkt);
    int ret;
    while (isPlay && (ret = av_read_frame(fmt_ctx, &pkt)) >= 0) {//bug 本地文件播放暂停后从什么位置开始继续播放
        AVPacket orig_pkt = pkt;
        if (pkt.stream_index == audio->stream_index && audio && audio->isPlay) {
            audio->put(&pkt);
        } else if (pkt.stream_index == video->stream_index && video && video->isPlay) {
            video->put(&pkt);
        }
        av_packet_unref(&orig_pkt);
    }
    av_packet_unref(&pkt);
    LOG_E(TAG, "%s, 结果：%s, code:%d\n", "入隊列完成", av_err2str(ret), ret);
    return 0;
}

void put_packet_que() {
    isPlay = 1;//生产标志位
    pthread_t thread_t;
    pthread_create(&thread_t, NULL, read_forms, NULL);
    pthread_detach(thread_t);
}

int open_codec_context(AVMediaType media_type, MultiMedia *media) {
    int ret = 0;
    ret = av_find_best_stream(fmt_ctx, media_type, -1, -1, NULL, 0);
    if (ret < 0) {
        LOG_E(TAG, "Could not find %s stream in input file audio\n",
              av_get_media_type_string(media_type));
        return ret;
    } else {
        media->stream_index = ret;
        AVStream *stream = fmt_ctx->streams[ret];
        media->time_base = stream->time_base;
        /* find decoder for the stream */
        AVCodec *dec = avcodec_find_decoder(stream->codecpar->codec_id);
        if (!dec) {
            LOG_E(TAG, "Failed to find %s codec\n", avcodec_get_name(stream->codecpar->codec_id));
            return AVERROR(EINVAL);
        }
        /* Allocate a codec context for the decoder */
        AVCodecContext *dec_ctx = avcodec_alloc_context3(dec);
        if (!dec_ctx) {
            LOG_E(TAG, "Failed to allocate the %s codec context\n",
                  av_get_media_type_string(media_type));
            return AVERROR(ENOMEM);
        }
        /* Copy codec parameters from input stream to output codec context */
        if ((ret = avcodec_parameters_to_context(dec_ctx, stream->codecpar)) < 0) {
            LOG_E(TAG, "Failed to copy %s codec parameters to decoder context\n",
                  av_get_media_type_string(media_type));
            return ret;
        }

        /* Init the decoders, with or without reference counting , AVCodecContext.refcounted_frames
         * AVCodecContext.refcounted_frames表示该frame的引用计数，当这个值为1时，表示有另外一帧将该帧用作参考帧，
         * 而且参考帧返回给调用者；当参考完成时，调用者需要调用av_frame_unref()方法解除对该帧的参考
         * */
        AVDictionary *opts = NULL;
        av_dict_set(&opts, "refcounted_frames", "0", 0);

        if ((ret = avcodec_open2(dec_ctx, dec, &opts)) < 0) {
            LOG_E(TAG, "Failed to open %s codec\n", av_get_media_type_string(media_type));
            return ret;
        }
        av_dict_free(&opts);
        if (media_type == AVMEDIA_TYPE_VIDEO) {
//            dec_ctx->width = ((VideoMedia *) media)->dst_pic_w;
//            dec_ctx->height = ((VideoMedia *) media)->dst_pic_h;
        }
        media->dec_ctx = dec_ctx;

    }
    return ret;
}


void release() {
    isPlay = 0;
    video->setIsPlay(0);
    audio->setIsPlay(0);
    ANativeWindow_release(window);
    avformat_free_context(fmt_ctx);
    delete (audio);
    delete (video);
}